#ifndef GAME_ANIMATION_GRID_GRID_MANAGER_H
#define GAME_ANIMATION_GRID_GRID_MANAGER_H
//
// Game Animation.
// Copyright (C) 2007 Department of Computer Science, University of Copenhagen
//
#include <animation/configuration.h>

#include <vector>
#include <cmath>
#include <cassert>

namespace animation
{

  /**
   * Grid Manager Class.
   *
   * Extents a grid with the functionality to recycle grid cells when virtual grid is moving around.
   */
  template<typename types>
  class GridManager : public types::grid_type
  {
  public:

    typedef typename types::grid_type         base_class;
    typedef typename types::index_type        index_type;
    typedef typename types::vector3_type      vector3_type;
    typedef typename types::real_type         real_type;
    typedef typename types::grid_type         grid_type;
    typedef typename grid_type::cell_iterator cell_iterator;
    typedef typename types::cell_type         cell_type;

  protected:

    index_type m_band;  ///< Number of loades cells along axis directions, which is kept loaded.

  public:

    /**
     * Initialization.
     *
     * @param I                 Number of grid cells along x-axis.
     * @param J                 Number of grid cells along y-axis.
     * @param dx                The length of the grid cells along the x-axis.
     * @param dy                The length of the grid cells along the y-axis.
     * @param cell_generator    The algorithm used to fill in layer geometries of the cells.
     */
    template<typename cell_generator>
    void init(index_type const & I,index_type const & J,real_type const & dx,real_type const & dy, cell_generator & generator)
    {
      base_class::init(I,J,dx,dy);
      m_band = I/2;

      for(index_type t = 0;t<J;++t)
        for(index_type s = 0;s<I;++s)
        {
          cell_type & cell = this->cell(s,t);
          cell.clear();
          cell.s() = s;
          cell.t() = t;
          cell.owner() = this;
          cell.i() = this->i() + ( s - this->s() );
          cell.j() = this->j() + ( t - this->t() );
          generator(cell);
        }
    }

    /**
     * Place Avatar.
     * This method places the avatar in the middle of the virtual grid.
     *
     * @param position        Position used to place avatar in grid, note only x and y components are used for this.
     * @param generator       The algorithm that should be used to fill in new cells if grid location is updated.
     */
    template<typename cell_generator>
    void place(vector3_type const & position, cell_generator & generator)
    {
      //--- Determine new grid location and displacement from old grid location
      index_type  old_i  = this->i();
      index_type  old_j  = this->j();
      index_type  new_i  = this->get_i(position(0));
      index_type  new_j  = this->get_j(position(1));
      index_type  disp_i = new_i - old_i;
      index_type  disp_j = new_j - old_j;

      if(disp_i==0 && disp_j==0)
        return;

      //index_type old_s = this->s();
      //index_type old_t = this->t();
      index_type new_s = this->get_s( disp_i );
      index_type new_t = this->get_t( disp_j );

      //--- Find cells that are outside the band around new grid location
      cell_iterator cell = this->begin();
      cell_iterator end  = this->end();
      for(;cell!=end;++cell)
      {
        //--- out of range test using global indices
        bool out_of_i_range = std::abs(   cell->i() - new_i  ) > m_band;
        bool out_of_j_range = std::abs(   cell->j() - new_j  ) > m_band;
        if( out_of_i_range || out_of_j_range)
        {
          int delta_i = ( cell->s() - new_s );
          if(delta_i>m_band)
            delta_i = delta_i - this->I();
          if(delta_i<-m_band)
            delta_i = delta_i + this->I();
          cell->i() = new_i + delta_i;
          int delta_j = ( cell->t() - new_t );
          if(delta_j>m_band)
            delta_j = delta_j - this->J();
          if(delta_j<-m_band)
            delta_j = delta_j + this->J();
          cell->j() = new_j + delta_j;
          generator(*cell);
        }
      }
     //--- update grid location information
     this->i() = new_i;
     this->j() = new_j;
     this->s() = new_s;
     this->t() = new_t;
    }
  };

} // namespace animation

// GAME_ANIMATION_GRID_GRID_MANAGER_H
#endif
